/*******************************************************************************
 * Copyright (c) 2000, 2005 IBM Corporation and others. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
 * and is available at http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors: IBM Corporation - initial API and implementation
 ******************************************************************************/
package org.dftproject.genesis.ui.pages.summary.sources;

import java.util.Iterator;

import org.dftproject.genesis.ui.figures.IHyperlinkListener;
import org.eclipse.draw2d.Cursors;
import org.eclipse.draw2d.FigureUtilities;
import org.eclipse.draw2d.Graphics;
import org.eclipse.draw2d.KeyEvent;
import org.eclipse.draw2d.KeyListener;
import org.eclipse.draw2d.MouseEvent;
import org.eclipse.draw2d.MouseListener;
import org.eclipse.draw2d.MouseMotionListener;
import org.eclipse.draw2d.geometry.Dimension;
import org.eclipse.draw2d.text.BidiInfo;
import org.eclipse.draw2d.text.TextFlow;
import org.eclipse.draw2d.text.TextFragmentBox;
import org.eclipse.jface.resource.JFaceColors;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.graphics.FontMetrics;
import org.eclipse.swt.graphics.TextLayout;

/**
 * An inline flow figure that renders a string of text across one or more lines.
 * A TextFlow can not contain children. All <code>InlineFlow</code> figure's
 * must be parented by a <code>FlowFigure</code>.
 * <p>
 * WARNING: This class is not intended to be subclassed by clients.
 * @author hudsonr
 * @author Pratik Shah
 * @since 2.1
 */
public class HyperlinkFlow extends TextFlow {
	
	public static final Color inactiveColor = JFaceColors.getHyperlinkText(null);
	public static final Color activeColor = JFaceColors.getActiveHyperlinkText(null);

	private boolean hover;
	private boolean alwaysUnderline = true;
	private Object data;

	protected static final String ELLIPSIS = "...";
	protected BidiInfo bidiInfo;

	public HyperlinkFlow() {
		this(new String());
	}
	
	public HyperlinkFlow(String text) {
		super(text);
		
		setCursor(Cursors.HAND);
		addListeners();
	}

	protected void addListeners() {
		MouseTracker tracker = new MouseTracker();
		addMouseMotionListener(tracker);
		addMouseListener(tracker);
		addKeyListener(new KeyListener.Stub() {

			@Override
			public void keyReleased(KeyEvent ke) {
				if (ke.keycode == SWT.CR)
					handleActivate(SWT.NONE);
			}
			
		});
	}

	protected boolean getHover() {
		return hover;
	}

	protected void setHover(boolean hover) {
		this.hover = hover;
	}

	public void addHyperlinkListener(IHyperlinkListener listener) {
		addListener(IHyperlinkListener.class, listener);
	}

	public void removeHyperlinkListener(IHyperlinkListener listener) {
		removeListener(IHyperlinkListener.class, listener);
	}

	protected void handleActivate(int state) {
		Iterator<?> iter = getListeners(IHyperlinkListener.class);
		while (iter.hasNext())
			((IHyperlinkListener) iter.next()).linkActivated(data, state);
	}

	protected class MouseTracker extends MouseMotionListener.Stub implements MouseListener {

		private boolean mouseDown;

		public void mousePressed(MouseEvent me) {
			if (me.button == 1) {
				me.consume();
	
				mouseDown = true;
			}
		}

		public void mouseReleased(MouseEvent me) {
			if (mouseDown && me.button == 1) {	
				if (getBounds().contains(me.getLocation()))
					handleActivate(me.getState());
				
				mouseDown = false;
			}
		}
		
		public void mouseDoubleClicked(MouseEvent me) {
		}

		@Override
		public void mouseEntered(MouseEvent me) {
			setHover(true);
			repaint();
		}

		@Override
		public void mouseExited(MouseEvent me) {
			setHover(false);
			repaint();
		}

		@Override
		public void mouseDragged(MouseEvent me) {
			setHover(getBounds().contains(me.getLocation()));
			repaint();
		}

	}

	public Object getData() {
		return data;
	}

	public void setData(Object data) {
		this.data = data;
	}

	public boolean isAlwaysUnderline() {
		return alwaysUnderline;
	}

	public void setAlwaysUnderline(boolean alwaysUnderline) {
		this.alwaysUnderline = alwaysUnderline;
	}

	@Override
	public void paint(Graphics graphics) {
		graphics.setForegroundColor(hover || hasFocus() ? activeColor : inactiveColor);
		super.paint(graphics);
	}

	@Override
	protected void paintFigure(Graphics graphics) {
		for (int i = 0; i < getFragments().size(); i++) {
			TextFragmentBox fragment = (TextFragmentBox) getFragments().get(i);

			String draw = getBidiSubstring(fragment, i);
			if (fragment.isTruncated())
				draw += ELLIPSIS;

			FontMetrics fm = FigureUtilities.getFontMetrics(getFont());
			int ascent = fm.getHeight() - fm.getDescent();
			paintText(graphics, draw, fragment.getX(), fragment.getBaseline() - ascent, fragment.getBidiLevel());
		}
	}

	protected void paintText(Graphics graphics, String draw, int x, int y, int bidiLevel) {
		if (bidiLevel == -1) {
			graphics.drawString(draw, x, y);
			if (alwaysUnderline || hover) {
				Dimension size = FigureUtilities.getStringExtents(draw, getFont());
				graphics.drawLine(x, y + size.height - 1, x + size.width, y + size.height - 1);
			}
		} else {
			TextLayout tl = FlowUtilities.getTextLayout();
			if (isMirrored())
				tl.setOrientation(SWT.RIGHT_TO_LEFT);
			tl.setFont(graphics.getFont());
			tl.setText(draw);
			graphics.drawTextLayout(tl, x, y);
		}
	}

	protected String getBidiSubstring(TextFragmentBox box, int index) {
		if (box.getBidiLevel() < 1)
			return getText().substring(box.offset, box.offset + box.length);

		StringBuffer buffer = new StringBuffer(box.length + 3);
		buffer.append(box.isRightToLeft() ? BidiChars.RLO : BidiChars.LRO);
		if (index == 0 && bidiInfo.leadingJoiner)
			buffer.append(BidiChars.ZWJ);
		buffer.append(getText().substring(box.offset, box.offset + box.length));
		if (index == getFragments().size() - 1 && bidiInfo.trailingJoiner)
			buffer.append(BidiChars.ZWJ);
		return buffer.toString();
	}

	protected class BidiChars {

		static final char P_SEP = '\u2029';
		static final char ZWJ = '\u200d';
		static final char LRO = '\u202d';
		static final char RLO = '\u202e';
		static final char OBJ = '\ufffc';
		static final char LRE = '\u202a';
		static final char RLE = '\u202b';

	}

}